package com.justgame.project.cloud.common.util;


import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis 工具类
 *
 * @Author ZhouWeiBo
 */
@Component
@AllArgsConstructor
public class RedisUtil {

    /**
     * 默认过期时长，单位：秒
     */
    public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
    /**
     * 不设置过期时长
     */
    public final static long NOT_EXPIRE = 99999999L;
    private final static Gson gson = new Gson();
    private final static ObjectMapper om = new ObjectMapper();
    private final RedisTemplate<String, Object> redisTemplate;
    private final ValueOperations<String, String> valueOperations;
    private final HashOperations<String, Object, Object> hashOperations;
    private final ListOperations<String, Object> listOperations;
    private final SetOperations<String, Object> setOperations;
    private final ZSetOperations<String, Object> zSetOperations;

    public void set(String key, String value, long expire) {
        valueOperations.set(key, value);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    public void hSet(String key, Map<Object, Object> value, long expire) {
        hashOperations.putAll(key, value);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    /**----------------zSet相关操作------------------*/
    /**
     * 添加元素,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zAdd(String key, String value, double score) {
        return zSetOperations.add(key, value, score);
    }

    public Long zAdd(String key, Set<ZSetOperations.TypedTuple<Object>> values) {
        return zSetOperations.add(key, values);
    }

    public Long zRemove(String key, Object... values) {
        return zSetOperations.remove(key, values);
    }

    /**
     * 增加元素的score值，并返回增加后的值
     * @return
     */
    public Double zIncrementScore(String key, Object value, double delta) {
        return zSetOperations.incrementScore(key, value, delta);
    }

    /**
     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
     * @param key
     * @param value
     * @return
     */
    public Long zRank(String key, Object value) {
        return zSetOperations.rank(key, value);
    }

    /**
     * 返回元素在集合的排名,按元素的score值由大到小排列
     * @param key
     * @param value
     * @return
     */
    public Long zReverseRank(String key, Object value) {
        return zSetOperations.reverseRank(key ,value);
    }

    /**
     * 获取集合的元素, 从小到大排序
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> zRange(String key, long start, long end) {
        return zSetOperations.range(key, start, end);
    }

    /**
     * 获取集合元素, 并且把score值也获取
     */
    public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, long start, long end) {
        return zSetOperations.rangeWithScores(key, start, end);
    }

    /**
     * 根据Score值查询集合元素
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<Object> zRangeByScore(String key, double min, double max) {
        return zSetOperations.rangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从小到大排序
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<ZSetOperations.TypedTuple<Object>> zRangeByScoreWithScores(String key, double min, double max) {
        return zSetOperations.rangeByScoreWithScores(key, min, max);
    }

    public Set<ZSetOperations.TypedTuple<Object>> zRangeByScoreWithScores(String key, double min, double max, long start, long end) {
        return zSetOperations.rangeByScoreWithScores(key, min, max, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> zReverseRange(String key, long start, long end) {
        return zSetOperations.reverseRange(key, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序, 并返回score值
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<Object>> zReverseRangeWithScores(String key, long start, long end) {
        return zSetOperations.reverseRangeWithScores(key, start, end);
    }

    public <T> T hGet(String key, Object field) {
        return (T) this.hashOperations.get(key, field);
    }

    public <T> Set<T> hKeys(String key) {
        return (Set<T>) this.hashOperations.keys(key);
    }


    public void hSet(String key, Map<Object, Object> value) {
        hashOperations.putAll(key, value);
    }

    public void hSet(String key,Object hashKey, Object value) {
        hashOperations.put(key, hashKey, value);
    }


    public Long hDelete(String key, Object... keys) {
        return hashOperations.delete(key, keys);
    }

    public void sSet(String key, Object... values) {
        setOperations.add(key, values);
    }

    public <T>  void sSet(String key, Set<T> set) {
        set.forEach( value -> {
            setOperations.add(key, value);
        });
    }

    public <T> Set<T> sGet(String key) {
        return (Set<T>) setOperations.members(key);
    }

    public <T> Set<T> sRemoveAll(String key) {
        return (Set<T>) setOperations.pop(key, setOperations.size(key));
    }

    /**
     * redisTemplate删除迷糊匹配的key的缓存
     */
    public int delKeys(String key) {
        if (StringUtils.isNotBlank(key) && !key.endsWith("*")) key += "*";
        Set keys = redisTemplate.keys(key);
        if (CollectionUtils.isNotEmpty(keys)) {
            redisTemplate.delete(keys);
            return keys.size();
        }
        return 0;
    }

    public Boolean exists(String key) {
        return this.redisTemplate.hasKey(key);
    }

    public Boolean hExists(String key, Object field) {
        return this.hashOperations.hasKey(key, field);
    }

    public void set(String key, Object value, long expire) {
        valueOperations.set(key, JSON.toJSONString(value), expire, TimeUnit.SECONDS);
    }


    public void set(String key, Object value) {
        set(key, value, DEFAULT_EXPIRE);
    }

    public <T> List<T> getCollection(String key) {
        return (List<T>) listOperations.range(key, 0, -1);
    }

    public <T> T get(String key, Class<T> clazz, long expire) {
        String value = valueOperations.get(key);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value == null ? null : fromJson(value, clazz);
    }

    public <T> Long lRightPush(String key, List<T> list, long expire) {
        Long l = listOperations.rightPushAll(key, list);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return l;
    }

    public Long lRightPush(String key, Object obj, long expire) {
        Long l = redisTemplate.opsForList().rightPush(key, obj);
        redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        return l;
    }


    public <T> T get(String key, Class<T> clazz) {
        return get(key, clazz, NOT_EXPIRE);
    }

    public String get(String key, long expire) {
        String value = valueOperations.get(key);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value;
    }

    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    public String get(String key) {
        return get(key, NOT_EXPIRE);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * JSON数据，转成Object
     */
    private <T> T fromJson(String json, Class<T> clazz) {
        return gson.fromJson(json, clazz);
    }
}
